
package com.cougil.uoc.seghismed.utils;

import iaik.asn1.ObjectID;
import iaik.asn1.structures.Name;
import iaik.security.provider.IAIK;
import iaik.x509.X509Certificate;

import java.security.Security;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.log4j.Logger;

import com.cougil.uoc.seghismed.business.CipherService;
import com.cougil.uoc.seghismed.cipher.P12;
import com.cougil.uoc.seghismed.model.Metge;
import com.cougil.uoc.seghismed.model.Pacient;
import com.cougil.uoc.seghismed.model.Usuari;
import com.cougil.uoc.seghismed.model.Visita;
import com.cougil.uoc.seghismed.model.enums.UnitatOrganitzativa;
import com.opensymphony.xwork2.util.LocalizedTextUtil;


/**
 * Utilitats generals per Seghismed
 * @author Ignacio Cougil Jares
 */
public class SeghismedUtils {

	public static final int ANYWAY = 2;

	private static Locale CATALA = new Locale("ca", "ES"); //Locale.getDefault()

	static final byte[] HEX_CHAR_TABLE = {
	    (byte)'0', (byte)'1', (byte)'2', (byte)'3',
	    (byte)'4', (byte)'5', (byte)'6', (byte)'7',
	    (byte)'8', (byte)'9', (byte)'a', (byte)'b',
	    (byte)'c', (byte)'d', (byte)'e', (byte)'f'
	  };

	private static final char[] kDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
	      'b', 'c', 'd', 'e', 'f' };

	private static final Logger logger = Logger.getLogger(SeghismedUtils.class);

	public static String LONG_DATE_AND_HOUR = "dd/MM/yy HH:mm";
	public static String LONG_HOUR_AND_DATE = "HH:mm:ss dd/MM/yyyy";

	public static final int MAYUSCULA = 0;
	public static final int MINUSCULA = 1;

	private static NumberFormat nf = NumberFormat.getInstance(SeghismedUtils.CATALA);

	private static final String NIF_STRING = "TRWAGMYFPDXBNJZSQVHLCKET";

	private static Random rand = new Random();
	private static SimpleDateFormat sdf = new SimpleDateFormat();


	/**
	 * Converteix un array de bytes a hexadecimals
	 *
	 * @param bytes {@link Array} array de bytes a convertir
	 * @return String amb la cadena d'hexadecimals convertits
	 */
	public static String bytesToHexadecimal(byte[] bytes) {
		return HexUtils.convertBytes(bytes);
	}

	/**
	 * Retorna la representació en cadena d'una data en el format emprat per defecte
	 *
	 * @param date
	 *            {@link Date} data de la que es preten obtenir el seu formateig
	 * @return cadena que representa l'objecte informat o null en altre cas
	 */
	public static String formatDate(Date date) {
		return SeghismedUtils.formatDate(date, SeghismedUtils.LONG_HOUR_AND_DATE);
	}

	/**
	 * Retorna la cadena de text que representa la data amb el format/patró indicat
	 *
	 * @param date
	 *            {@link Date} data de la que es preten obtenir tota la seva informació
	 * @param pattern
	 *            {@link String} patró/format de la cadena de la data que es preten obtenir
	 * @return cadena {@link String} que representa la data informada o null en altre cas
	 */
	public synchronized static String formatDate(Date date, String pattern) {
		SeghismedUtils.sdf.applyPattern(pattern);
		return date == null ? null : SeghismedUtils.sdf.format(date);
	}
	/**
	 * Retorna la representació en cadena d'un nombre sencer
	 *
	 * @param integer
	 *            {@link Integer} sencer del que es preten obtenir el seu formateig
	 * @return cadena que representa l'objecte informat o null en altre cas
	 */
	public static String formatInteger(Integer integer) {
		return integer == null ? null : SeghismedUtils.nf.format(integer.intValue());
	}

	public static Actor getAppActor() {
		String appNameActor = SeghismedUtils.getProperty("appName.actor");
		return Actor.valueOf(appNameActor);
	}


	public static byte[] getBytes(String message) {
		return (message != null ? message.getBytes() : new byte[0] );
	}

	public static String getDefPassword() {
		return SeghismedUtils.getProperty("def.password");
	}

	public static String getFileCertificate(Usuari u) {
		return SeghismedUtils.getProperty(
				SeghismedUtils.getPrefixFileName(u.getUnitatOrganitzativa())+".crt");
	}

	public static String getFileP12(Usuari u) {
		return SeghismedUtils.getProperty(
				SeghismedUtils.getPrefixFileName(u.getUnitatOrganitzativa())+".p12");
	}

	/**
	 * Retorna la lletra del DNI
	 * @param strDNI dni del que es preten obtenir la lletra
	 * @return NIF complert
	 */
	public static char getLletraDNI(String strDNI) {
		int dni = Integer.parseInt(strDNI);
		return SeghismedUtils.NIF_STRING.charAt(dni % 23);
	}

	/**
	 * Retorna el prefixe del nom de fitxer associat a l'usuari. En una aplicació real,
	 * possiblement caldria ofuscar aquest valor o calcular-lo segons algun algoritme
	 * one-way de xifrat per blocs/flux o similar
	 *
	 * @param unitat
	 *            {@link UnitatOrganitzativa} per al qual es preten obtenir el prefix del
	 *            nom del fitxer que li pertoca
	 * @return String amb el prefix trobat
	 */
	private static String getPrefixFileName(UnitatOrganitzativa unitat) {
		String fileName = null;
		if (UnitatOrganitzativa.Gestio.equals(unitat)) {
			fileName = "gestor";
		} else if (UnitatOrganitzativa.Metges.equals(unitat)) {
			fileName = "metge";
		} else if (UnitatOrganitzativa.Pacients.equals(unitat)) {
			fileName = "pacient";
		} else if (UnitatOrganitzativa.PFC_Seguretat.equals(unitat)) {
			fileName = "ca";
		}
		return fileName;
	}

	/**
	 * Retorna la cadena trobada al fitxer de recursos emprat per defecte en el sistema
	 * (Català)
	 *
	 * @param key
	 *            Clau de cadena a cercar
	 * @return El valor trobat o null en cas contrari
	 */
	public static String getProperty(String key) {
		/*
		Locale locales[] = Locale.getAvailableLocales();
		for (int i = 0; i < locales.length; i++) {
			logger.debug(locales[i]);
		}
		 */
		String value = LocalizedTextUtil.findDefaultText(key, SeghismedUtils.CATALA);
		if (value == null) {
			throw new SeghismedException("No s'ha pogut trobar la propietat '"+
					key+"' en el fitxer de recursos per defecte");
		} else {
			return value;
		}
	}

	/**
	 * Retorna un boleà de forma pseudoaleatòria
	 *
	 * @return bolèa generat
	 * @see java.util.Random#nextBoolean()
	 */
	public static boolean getRandomBoolean() {
		return SeghismedUtils.rand.nextBoolean();
	}

	/**
	 * Retorna un caràcter aleatòri, és a dir, o bé en majúscules o en minúscules
	 *
	 * @return caràcter generat
	 */
	public static char getRandomChar() {
		return SeghismedUtils.getRandomChar(SeghismedUtils.ANYWAY);
	}

	/**
	 * Retorna un caràcter segons l'aleatorietat informada segons la constant informada.
	 * És a dir la constant informada haurà de permetre distingir si es vol retornar el
	 * caràcter, o bé en majúscules o en minúscules
	 *
	 * @return caràcter generat
	 */
	public static char getRandomChar(final int CONSTANT) {
		char ch1, ch2;
		switch (CONSTANT) {
			case MAYUSCULA:
				ch1 = 'A';
				ch2 = 'Z';
				break;
			case MINUSCULA:
				ch1 = 'a';
				ch2 = 'z';
				break;
			default:
				if (SeghismedUtils.getRandomBoolean()) {
					return SeghismedUtils.getRandomChar(SeghismedUtils.MAYUSCULA);
				} else {
					return SeghismedUtils.getRandomChar(SeghismedUtils.MINUSCULA);
				}
		}
		return (char) (ch1 + SeghismedUtils.rand.nextDouble() * (ch2 - ch1 + 1));
	}

	public static Date getRandomDate() {
		Date now = new Date();
		// com a mínim una setmana endavant (1000 milisegons * 60 segons * 60 minuts * 24 minuts * 7 dies)
		final long minim = (1000 * 60 * 60 * 24 * 7);
		long random = SeghismedUtils.getRandomInt(Integer.MAX_VALUE) + minim;
		return new Date(now.getTime() + random);
	}

	/**
	 * Retorna un sencer de forma pseudoaleatòria
	 *
	 * @return sencer generat
	 * @see java.util.Random#nextInt()
	 */
	public static int getRandomInt() {
		return SeghismedUtils.rand.nextInt();
	}

	/**
	 * Retorna un sencer de forma pseudoaleatòria, comprés entre 0 (inclòs) fins el sencer
	 * informat (exclòs)
	 *
	 * @param n
	 *            sencer que indica el màxim número + 1 que es pot remetre
	 * @return sencer generat
	 * @see java.util.Random#nextInt(int)
	 */
	public static int getRandomInt(int n) {
		return SeghismedUtils.rand.nextInt(n);
	}

	/**
	 * Retorna una cadena aleatòria de tamany l'informat
	 *
	 * @param length
	 *            tamany de la cadena a crear
	 * @return cadena obtinguda
	 */
	public static String getRandomString(int length) {
		StringBuffer sb = new StringBuffer();
		while (sb.length() != length) {
			sb = sb.append(SeghismedUtils.getRandomChar());
		}
		return sb.toString();
	}

	public static String getString(byte[] message) {
		return (message != null ? new String(message) : null );
	}

	/**
	 * Retorna l'usuari Gestor representat dintre de l'aplicació. Donat que aquest usuari
	 * no es troba emmagatzemat a la BBDD s'ha de poder instanciar correctament, per
	 * exemple, també des dels aplicatius clients. Les seves dades identificatives són
	 * bàsicament el seu DNI i Unitat Organitzava que es troben inclosos dintre del seu
	 * certificat i que és amb aquest valor que el cercarem. En una aplicació real,
	 * possiblement s'hauria d'ofuscar aquesta informació i/o anar-la a cercar d'una altra
	 * forma
	 *
	 * @param cipherService
	 *            {@link CipherService} servei encarregat de carregar en memòria les
	 *            intàncies de certificats i contenidos PKCS#12
	 * @return Usuari trobat amb les seves dades carregades (DNI, nom, Unitat
	 *         Organitzativa) o null en cas contrari
	 */
	public static Usuari getUsuariGestor(CipherService cipherService) {
		// creem una instància per defecte
		Usuari u = new Usuari();

		/*
		 * Les dades del certificat del gestor són les següents: DNI: 00000000-A - Gestor -
		 * gestor@seghismed.uoc.edu - OrganizationalUnit: Gestio Per tal que el servei
		 * pugui trobar l'usuari, li assignem el seu DNI i la seva Unitat Organitzativa
		 */
		//u.setDni("00000000-A");
		u.setUnitatOrganitzativa(UnitatOrganitzativa.Gestio);

		X509Certificate certif = null;

		// recuperem el certificat públic del contenidor .P12: es tracta de l'aplicatiu
		// Gestor
		if (Actor.GESTOR.equals(SeghismedUtils.getAppActor())) {
			P12 contenidor = cipherService.getP12(u);
			certif = contenidor.getCertificate();

			// sinó, recuperem el certificat públic del fitxer .crt: es tracta d'un dels
			// aplicatius Client
		} else {
			certif = cipherService.getX509(u);
		}

		Name name = (Name) certif.getSubjectDN();
		u.setNom(name.getRDN(ObjectID.commonName));
		u.setDni(name.getRDN(ObjectID.dnQualifier));
		//u.setPrimerCognom( name.getRDN(ObjectID.surName) ); // <-- null
		u.setUnitatOrganitzativa(UnitatOrganitzativa.Gestio);
		u.setCertificat(certif.toByteArray());

		return u;
	}

	/**
	 * Converteix un array d'hexadecimals a bytes
	 *
	 * @param hexadecimals
	 *            {@link String} cadena amb el contingut en hexadecimal
	 * @return Array de bytes
	 */
	public static byte[] hexadecimalsToBytes(String hexadecimals) {
		return HexUtils.convertHexadecimals(hexadecimals);
	}

	/**
	 * Inicialitza l'entorn, afegint IAIK com a proveïdor de seguretat
	 */
	public static void initIAIK() {
		IAIK provider = new IAIK();
		String providerName = provider.getName();
		if ( Security.getProvider(providerName) != null ) {
			// tot OK! ja està instal·lat!!
			//Security.removeProvider(providerName);
			//logger.debug(providerName+" eliminat satisfactoriament");
			SeghismedUtils.logger.info(providerName + " ja instal·lat anteriorment!");
		} else {
			Security.insertProviderAt(provider, 1);
			SeghismedUtils.logger.info(providerName + " instal·lat satisfactoriament");
		}
	}

	public static boolean isGestor(Usuari usuari) {
		return UnitatOrganitzativa.Gestio.equals(usuari.getUnitatOrganitzativa());
	}

	/**
	 * Retorna si les llistes són iguals, verificant el nombre d'elements que tenen
	 * cadascuna. Primerament verifica si el nombre d'elements inclosos és el mateix i
	 * després si tots els elements inclossos dintre una de l'altra coincideix
	 *
	 * @param llista1
	 *            {@link List} primera llista a comparar
	 * @param llista2
	 *            {@link List} segona llista amb la que comparar
	 * @return true o false depenent de si les llistes són iguals o no
	 */
	@SuppressWarnings("unchecked")
	public static boolean isLlistesIguals(List llista1, List llista2) {
		return (llista1.size() == llista2.size()) && llista1.containsAll(llista2)
		&& llista2.containsAll(llista1);
	}

	public static boolean isMetge(Usuari usuari) {
		return UnitatOrganitzativa.Metges.equals(usuari.getUnitatOrganitzativa());
	}

	public static boolean isPacient(Usuari usuari) {
		return UnitatOrganitzativa.Pacients.equals(usuari.getUnitatOrganitzativa());
	}

	public static Usuari loadUserCert(CipherService cipherService, boolean fromP12) {
		Usuari usuari = null;
		Actor appActor = SeghismedUtils.getAppActor();
		if (Actor.PACIENT.equals(appActor)) {
			usuari = new Pacient();
			usuari.setUnitatOrganitzativa(UnitatOrganitzativa.Pacients);
		} else if (Actor.METGE.equals(appActor)) {
			usuari = new Metge();
			usuari.setUnitatOrganitzativa(UnitatOrganitzativa.Metges);
		}

		X509Certificate cert = null;
		if (fromP12) {
			// obtenim la informació dels contenidors P12 (per defecte)
			P12 contenidor = cipherService.getP12(usuari);
			cert = contenidor.getCertificate();
		} else {
			cert = cipherService.getX509(usuari);
		}
		Name name = (Name) cert.getSubjectDN();
		usuari.setNom( name.getRDN(ObjectID.commonName) );
		//pacient.setPrimerCognom( name.getRDN(ObjectID.surName) ); // <-- null
		usuari.setDni( name.getRDN(ObjectID.dnQualifier) );
		usuari.setUnitatOrganitzativa(
				(UnitatOrganitzativa) SeghismedUtils.parseEnum(name.getRDN(ObjectID.organizationalUnit),
						UnitatOrganitzativa.values())
		);
		return usuari;
	}

	/**
	 * Afegeix en el fitxer de log la informació disponible del conjunt de metges amb accés
	 *
	 * @param prefix
	 *            {@link String} a afegir abans de mostrar el conjunt a mostrar
	 * @param metgesAmbAcces
	 *            {@link Map} conjunt de metges amb accés a mostrar
	 */
	public static void logMetges(String prefix, Map<Metge, Date> metgesAmbAcces) {
		SeghismedUtils.logger.debug(prefix);
		for (Iterator<Metge> iterator = metgesAmbAcces.keySet().iterator(); iterator.hasNext();) {
			Metge m = iterator.next();
			SeghismedUtils.logger.debug("Metge [" + m + "] fins la data ["+ metgesAmbAcces.get(m) + "]");
		}
	}

	/**
	 * Afegeix en el fitxer de log la informació disponible del conjunt de pacients
	 *
	 * @param prefix
	 *            {@link String} a afegir abans de mostrar el conjunt a mostrar
	 * @param pacients
	 *            {@link List} conjunt de pacients a mostrar
	 */
	public static void logPacients(String prefix, List<Pacient> pacients) {
		SeghismedUtils.logger.debug(prefix);
		for (Iterator<Pacient> iterator = pacients.iterator(); iterator.hasNext();) {
			Pacient p = iterator.next();
			SeghismedUtils.logger.debug("Pacient [" + p + "]");
		}
	}

	/**
	 * Afegeix en el fitxer de log la informació disponible del conjunt de visites
	 *
	 * @param prefix
	 *            {@link String} a afegir abans de mostrar el conjunt a mostrar
	 * @param visitesProtegides
	 *            {@link Set} conjunt de visites a mostrar
	 */
	public static void logVisites(String prefix, Set<Visita> visites) {
		SeghismedUtils.logger.debug(prefix);
		for (Iterator<Visita> iterator = visites.iterator(); iterator.hasNext();) {
			Visita v = iterator.next();
			SeghismedUtils.logger.debug("Visita [" + v + "]");
		}
	}

	/**
	 * Retorna la instanciació de la data que representa la cadena informada amb el format per
	 * defecte
	 *
	 * @param cadena
	 *            {@link String} de la que es preten obtenir la seva representació com a objecte
	 *            Date
	 * @return {@link Date} que representa l'objecte informat o null en altre cas
	 */
	public static Date parseDate(String cadena) {
		return SeghismedUtils.parseDate(cadena, SeghismedUtils.LONG_HOUR_AND_DATE);
	}

	/**
	 * Retorna la instanciació de la data que representa la cadena amb el format indicat
	 *
	 * @param cadena
	 *            {@link String} de la que es preten obtenir la seva representació com a objecte
	 *            Date
	 * @param pattern
	 *            {@link String} patró/format de la cadena de la data que es preten obtenir
	 * @return {@link Date} que representa l'objecte informat o null en altre cas
	 */
	public synchronized static Date parseDate(String cadena, String pattern) {
		try {
			SeghismedUtils.sdf.applyPattern(pattern);
			return cadena == null ? null : (Date) SeghismedUtils.sdf.parse(cadena);
		} catch (ParseException e) {
			throw new SeghismedException(e);
		}

	}

	/**
	 * Retorna la instanciació de l'enumerat, el nom del qual representa la cadena
	 * informada
	 *
	 * @param cadena
	 *            {@link String} de la que es preten obtenir el seu enumerat
	 * @return {@link Enum} que representa l'objecte informat o null en altre cas
	 */
	public static Enum parseEnum(String searchNomEnumerat, Enum[] enumerats) {
		Enum value = null;
		if ((enumerats.length > 0) && (enumerats[0] instanceof SeghismedEnum)) {
			for (Enum eActual : enumerats) {
				if ( ((SeghismedEnum)eActual).getNom().equals(searchNomEnumerat)) {
					value = eActual;
				}
			}
		}
		return value;
	}


	/**
	 * Retorna la instanciació del sencer que representa la cadena informada
	 *
	 * @param cadena
	 *            {@link String} de la que es preten obtenir el seu sencer
	 * @return {@link Integer} que representa l'objecte informat o null en altre cas
	 */
	public static Integer parseInteger(String cadena) {
		try {
			return cadena == null ? null : new Integer (((Long) SeghismedUtils.nf.parse(cadena)).intValue());
		} catch (ParseException e) {
			throw new SeghismedException(e);
		}
	}

	public static void showClassAbsolutePath(Class c) {
		String name = "."+c.getName()+".class";
		name = name.replace('.', '/');
		int pos = name.lastIndexOf('/');
		name = name.substring(0,pos)+"."+name.substring(pos+1);
		//ex: /com/cougil/uoc/seghismed/cipher/P12.class
		SeghismedUtils.logger.debug("Classe: "+name+" Path: "+SeghismedUtils.class.getResource(name));

	}

	/**
	 * Mostra informació relacionada amb el certificat informat
	 *
	 * @param cert
	 *            {@link X509Certificate} Certificat del que es preten mostra informació
	 */
	public static void showInfoCertificate(X509Certificate cert) {
		SeghismedUtils.logger.debug("---Start info certificate --- ");
		SeghismedUtils.logger.debug("Caduca: " + cert.getNotAfter());
		SeghismedUtils.logger.debug("S/N: " + cert.getSerialNumber());
		SeghismedUtils.logger.debug("* Issuer");
		SeghismedUtils.logger.debug("Distinguished Name: " + cert.getIssuerDN());

		Name name = (Name) cert.getSubjectDN();
		String commonName = name.getRDN(ObjectID.commonName);
		String surName = name.getRDN(ObjectID.surName);
		String email = name.getRDN(ObjectID.emailAddress);
		String dni = name.getRDN(ObjectID.dnQualifier);
		String country = name.getRDN(ObjectID.country);
		String stateprovince = name.getRDN(ObjectID.stateOrProvince);
		String locality = name.getRDN(ObjectID.locality);
		String organization = name.getRDN(ObjectID.organization);
		String organizationalUnit = name.getRDN(ObjectID.organizationalUnit);

		SeghismedUtils.logger.debug("* Subject");
		SeghismedUtils.logger.debug("CommonName: " + commonName);
		SeghismedUtils.logger.debug("SurName: " + surName);
		SeghismedUtils.logger.debug("Email: " + email);
		SeghismedUtils.logger.debug("DNI: " + dni);
		SeghismedUtils.logger.debug("Country: " + country);
		SeghismedUtils.logger.debug("State or province: " + stateprovince);
		SeghismedUtils.logger.debug("Locality: " + locality);
		SeghismedUtils.logger.debug("Organization: " + organization);
		SeghismedUtils.logger.debug("OrganizationalUnit: " + organizationalUnit);

		//logger.debug("toString(): " + cert.toString(true));
		//logger.debug("toByteArray(): " + cert.toByteArray());
		SeghismedUtils.logger.debug("---End info certificate --- ");
	}

	/**
	 * Retorna si les dues llistes informades són iguals o no, comparant si tots els seus valors es
	 * troben en ambdòs inclosos
	 *
	 * @param primeraLlista
	 *            primera llista {@link List} a comparar
	 * @param segonaLlista
	 *            segona llista {@link List} a comparar
	 * @return cert en cas que continguin els mateixos elements, fals en cas contrari
	 */
	@SuppressWarnings("unchecked")
	public static boolean sonIguals(List primeraLlista, List segonaLlista) {
		int tamanyPrimer = (primeraLlista != null) ? primeraLlista.size() : 0;
		int tamanySegon = (segonaLlista != null) ? segonaLlista.size() : 0;
		boolean siOno = (tamanyPrimer == tamanySegon);
		if (siOno && (tamanyPrimer > 0)) {
			siOno = primeraLlista.containsAll(segonaLlista) && segonaLlista.containsAll(primeraLlista);
		}
		return siOno;
	}

	/**
	 * Retorna si els dos Maps informats són iguals o no, comparant si tots els seus valors es
	 * troben en ambdòs inclosos
	 *
	 * @param primerMap
	 *            primer Map {@link Map} a comparar
	 * @param segonMap
	 *            segon Map {@link Map} a comparar
	 * @return cert en cas que continguin els mateixos elements, fals en cas contrari
	 */
	@SuppressWarnings("unchecked")
	public static boolean sonIguals(Map primerMap, Map segonMap) {
		return SeghismedUtils.sonIguals(primerMap.keySet(), segonMap.keySet())
		&&
		SeghismedUtils.sonIguals(primerMap.entrySet(), segonMap.entrySet());
	}

	/**
	 * Retorna si els dos metges informats són iguals o no, comparant tots els seus atributs
	 *
	 * @param primerMetge {@link Metge} primer metge a comparar
	 * @param segonMetge {@link Metge} segon metge a comparar
	 * @return cert en cas que siguin iguals, fals en cas contrari
	 */
	public static boolean sonIguals(Metge primerMetge, Metge segonMetge) {
		if (!(primerMetge instanceof Metge) || !(segonMetge instanceof Metge)) {
			return false;
		}
		return new EqualsBuilder()
		.appendSuper(primerMetge.equals(segonMetge))
		.append(primerMetge.getEspecialitat(), segonMetge.getEspecialitat())
		.append(primerMetge.getNumeroColegiat(), segonMetge.getNumeroColegiat())
		.append(true, SeghismedUtils.sonIguals(primerMetge.getLlistaPacientsProt(),
				segonMetge.getLlistaPacientsProt()))
				.isEquals();
	}

	/**
	 * Retorna si els dos pacients informats són iguals o no, comparant tots els seus atributs
	 *
	 * @param primerPacient
	 *            primerPacient {@link Pacient} primer pacient a comparar
	 * @param segonPacient
	 *            segonPacient {@link Pacient} segon pacient a comparar
	 * @return cert en cas que siguin iguals, fals en cas contrari
	 */
	public static boolean sonIguals(Pacient primerPacient, Pacient segonPacient) {
		if (!(primerPacient instanceof Pacient) || !(segonPacient instanceof Pacient)) {
			return false;
		}
		return new EqualsBuilder()
		.appendSuper(primerPacient.equals(segonPacient))
		.append(primerPacient.getGrupSanguini(), segonPacient.getGrupSanguini())
		.append(true, SeghismedUtils.sonIguals(primerPacient.getAlergies(), segonPacient.getAlergies()))
		.isEquals();
	}

	/**
	 * Retorna si els dos Sets informats són iguals o no, comparant si tots els seus valors es
	 * troben en ambdòs inclosos
	 *
	 * @param primerSet
	 *            primer Set {@link Set} a comparar
	 * @param segonSet
	 *            segon Set {@link Set} a comparar
	 * @return cert en cas que continguin els mateixos elements, fals en cas contrari
	 */
	@SuppressWarnings("unchecked")
	public static boolean sonIguals(Set primerSet, Set segonSet) {
		int tamanyPrimer = (primerSet != null) ? primerSet.size() : 0;
		int tamanySegon = (segonSet != null) ? segonSet.size() : 0;
		boolean siOno = (tamanyPrimer == tamanySegon);
		if (siOno && (tamanyPrimer > 0)) {
			siOno = primerSet.containsAll(segonSet) && segonSet.containsAll(primerSet);
		}
		return siOno;
	}

}
